package com.anjie.powerproject.strategy;

import com.anjie.powerproject.entity.GraphInfo;
import com.anjie.powerproject.entity.HistoryData;
import com.anjie.powerproject.entity.StationInfo;
import com.anjie.powerproject.entity.StrategyOutput;
import com.anjie.powerproject.service.*;
import com.anjie.powerproject.service.impl.GraphDevServiceImpl;
import com.anjie.powerproject.strategy.pojo.*;
import com.anjie.powerproject.strategy.pojo.device.BaseDevice;
import com.anjie.powerproject.strategy.pojo.device.ConsumeDevice;
import com.anjie.powerproject.strategy.pojo.device.StorageDevice;
import com.anjie.powerproject.strategy.pojo.device.SupplyDevice;
import com.anjie.powerproject.strategy.service.HistoryDataService;
import com.anjie.powerproject.strategy.service.StrategyOutService;
import com.anjie.powerproject.strategy.service.WeatherForecastService;
import com.anjie.powerproject.strategy.utils.DateUtils;
import com.anjie.powerproject.strategy.utils.ElectricityPriceUtil;
//import com.anjie.powerproject.strategy.utils.HolidayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import org.springframework.util.unit.DataUnit;

import java.io.IOException;
import java.text.ParseException;
import java.util.*;


/**
 * 工况策略生成类的基类，工况策略具体生成类应该继承该类（也可以不继承），该类主要放置所有工况策略生成类共有的一些方法
 * 以及一些共用的变量
 */
@Component
@PropertySource(value = {"classpath:strategy.properties"},
        ignoreResourceNotFound = false, encoding = "UTF-8", name = "strategy.properties")
public class BaseWorkStrategy {
    @Autowired
    private RelationshipService relationshipService;
    @Autowired
    private WeatherForecastService weatherForecastService;
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private StrategyOutService strategyOutService;
    @Autowired
    private HistoryDataService historyDataService;
    @Autowired
    private GraphInfoService graphInfoService;
    @Autowired
    private ElectricityPriceService electricityPriceService;
    @Autowired
    private GraphDevServiceImpl graphDevService;
    @Autowired
    private StationInfoService stationInfoService;
    // 电价列表
    @Value("${strategy.electricityPrice1}")
    private String electricityPriceStr;

    private ElectricityPrice electricityPrice;

//    private ElectricityPriceCollection electricityPriceCollection;

    // 舒适温度
    @Value("${strategy.comfortTem}")
    private double comfortTem;

    // 冰蓄能每小时储能量
    @Value("${strategy.iceStorageCapHour}")
    private double iceStorageCapHour;

    // 冰蓄能每小时放能量
    @Value("${strategy.iceStorageReleaseHour}")
    private double iceStorageReleaseHour;

    // 冰蓄能总蓄能量
    @Value("${strategy.iceStorageTotal}")
    private double iceStorageTotal;

    // 双工况冷水机组每小时供冷量
    @Value("${strategy.doubleMachineReleaseHour}")
    private double doubleMachineReleaseHour;

    // 常温冷水机组每小时供冷量
    @Value("${strategy.normalMachineReleaseHour}")
    private double normalMachineReleaseHour;

    @Value("${strategy.defaultHeightNode}")
    private String defaultHeightNode;

    // 空气源热泵机组

    public BaseWorkStrategy() {

    }

    public double getComfortTem() {
        return comfortTem;
    }

    public double getIceStorageCapHour() {
        return iceStorageCapHour;
    }

    public double getIceStorageReleaseHour() {
        return iceStorageReleaseHour;
    }

    public double getIceStorageTotal() {
        return iceStorageTotal;
    }

    public double getDoubleMachineReleaseHour() {
        return doubleMachineReleaseHour;
    }

    public double getNormalMachineReleaseHour() {
        return normalMachineReleaseHour;
    }

    public List<BaseDevice> getDevice(Integer graphId, Integer type) {
        return graphDevService.findSummerDevicesByGraphId(graphId,type);
    }

    /**
     * 获取当前图谱Id，被废弃
     * @return
     */
    @Deprecated
    public Integer getGraphIdDeprecated() throws ParseException {
        Date date = new Date();
        List<GraphInfo> graphInfoList = graphInfoService.getAllGraphInfo();
        int graphId = graphInfoList.get(0).getGraphId();
        for (GraphInfo g:graphInfoList){
            Date startTime = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + g.getGraphStartTime());
            Date endTime = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + g.getGraphEndTime());
            // 如果终止时间小于起始时间，说明属于跨年的情况
            if (endTime.before(startTime)) {
                Date middleTime = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "12-31");
                endTime.setYear(endTime.getYear() + 1);
                if (date.after(startTime) && date.before(middleTime) || date.before(endTime) && date.after(middleTime) || date.equals(middleTime)) {
                    graphId = g.getGraphId();
                }
            } else {
                if (date.after(startTime)&&date.before(endTime)){
                    graphId = g.getGraphId();
                    //break;
                }
            }
        }
        return graphId;
    }


    public Integer getGraphId() throws ParseException {
        // 目前的图谱不会出现时间重合的现象了，但是可能出现时间空缺的时候，这个时候默认获取上一张图谱
        Date date = new Date();
        List<GraphInfo> graphInfoList = graphInfoService.getAllGraphInfo();
        for (GraphInfo graphInfo : graphInfoList) {
            Date startTime = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + graphInfo.getGraphStartTime());
            startTime = DateUtils.plusOrMinusDay(startTime, -1).getTime();
            Date endTime = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + graphInfo.getGraphEndTime());
            endTime = DateUtils.plusOrMinusDay(endTime, 1).getTime();
            // 如果终止时间小于起始时间，说明属于跨年的情况
            if (endTime.before(startTime)) {
//                Date middleL = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "12-31");
//                Date middleR = DateUtils.ymdFormat.parse(1900 + date.getYear() + 1 + "-" + "01-01");
//                endTime.setYear(endTime.getYear() + 1);
//                if (date.after(startTime) && date.before(middleL) || date.before(endTime) && date.after(middleR) || date.equals(middleR) || date.equals(middleL)) {
//                    return graphInfo.getGraphId();
//                }
//                if (date.after(startTime) && date.before(endTime)) {
//                    return graphInfo.getGraphId();
//                }
            	if (date.after(startTime)) {
            		endTime.setYear(endTime.getYear() + 1);
            	}
            	else if(date.before(endTime)) {
            		startTime.setYear(startTime.getYear() - 1);
            	}
            	
            	if (date.after(startTime) && date.before(endTime)) {
                  return graphInfo.getGraphId();
                }
                
            } else {
                if (date.after(startTime)&&date.before(endTime)){
                    return graphInfo.getGraphId();
                }
            }
        }
        // 如果执行到这里，只能说明出现了空缺，找寻前一张图谱
        GraphInfo beforeGraphInfo = graphInfoService.findBeforeGraphInfo(date);
        // 原则上这里一定不为空
        assert beforeGraphInfo != null;

        return beforeGraphInfo.getGraphId();
    }

    //定义一个根据传入时间来找对应图谱的方法
    public Integer getGraphIdByDate(Date date) throws ParseException {
        // 目前的图谱不会出现时间重合的现象了，但是可能出现时间空缺的时候，这个时候默认获取上一张图谱
        //Date date = new Date();
        List<GraphInfo> graphInfoList = graphInfoService.getAllGraphInfo();
        for (GraphInfo graphInfo : graphInfoList) {
            Date startTime = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + graphInfo.getGraphStartTime());
            startTime = DateUtils.plusOrMinusDay(startTime, -1).getTime();
            Date endTime = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + graphInfo.getGraphEndTime());
            endTime = DateUtils.plusOrMinusDay(endTime, 1).getTime();
            // 如果终止时间小于起始时间，说明属于跨年的情况
            if (endTime.before(startTime)) {
//                Date middleL = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "12-31");
//                Date middleR = DateUtils.ymdFormat.parse(1900 + date.getYear() + 1 + "-" + "01-01");
//                endTime.setYear(endTime.getYear() + 1);
//                if (date.after(startTime) && date.before(middleL) || date.before(endTime) && date.after(middleR) || date.equals(middleR) || date.equals(middleL)) {
//                    return graphInfo.getGraphId();
//                }
//                if (date.after(startTime) && date.before(endTime)) {
//                    return graphInfo.getGraphId();
//                }
                if (date.after(startTime)) {
                    endTime.setYear(endTime.getYear() + 1);
                }
                else if(date.before(endTime)) {
                    startTime.setYear(startTime.getYear() - 1);
                }

                if (date.after(startTime) && date.before(endTime)) {
                    return graphInfo.getGraphId();
                }

            } else {
                if (date.after(startTime)&&date.before(endTime)){
                    return graphInfo.getGraphId();
                }
            }
        }
        // 如果执行到这里，只能说明出现了空缺，找寻前一张图谱
        GraphInfo beforeGraphInfo = graphInfoService.findBeforeGraphInfo(date);
        // 原则上这里一定不为空
        assert beforeGraphInfo != null;

        return beforeGraphInfo.getGraphId();
    }

    /**
     * 从数据库获取电价
     * @return
     */
    public ElectricityPrice getElectricityPrice() {
        if (electricityPrice == null) {
            electricityPrice = new ElectricityPrice(electricityPriceStr);
        }
//
//        if (electricityPriceCollection == null) {
//            electricityPriceCollection = electricityPriceService.getResultList(graphId);
//        }
        return electricityPrice;
    }

//    public ElectricityPriceUtil getElectricityUtil(int graphId) {
//        ElectricityPriceCollection electricityPriceCollection = electricityPriceService.getResultList(graphId);
//        return electricityPriceCollection.getUtils();
//    }

    /**
     * 获取需要默认高亮的设备Id
     * @return
     */
    public String[] getDefaultHeightNode() {
        return defaultHeightNode.split(",");
    }

    /**
     * 判断今天是周末还是节假日还是工作日
     * @param date
     * @return
     * @throws IOException
     */
//    public Integer isHolidayByDate(Date date) throws IOException {
//        return Integer.parseInt(HolidayUtils.isHoliday(date).toString());
//    }

    /**
     * 使用日期获取当天所有的天气数据（预测数据）
     * @param date
     * @return
     */
    public DayWeather getForecastWeatherByDate(Date date) {
        List<StationInfo> infos = stationInfoService.findAllInfo();
        String cityCode = "6587794";//当表中没有数据时，设置默认的
        if(infos != null){
            cityCode = infos.get(0).getCityCode();//有多条数据时，默认获取第一条
        }
        return weatherForecastService.getForecastDayWeatherByDate(date,cityCode);
    }

    /**
     * 获取设备信息
     * @param graphId
     * @return
     */
    public Map<String, String> getDeviceInfo(Integer graphId) {
        return relationshipService.findDeviceInfoByGraphId(graphId);
    }

    public Map<Double, StrategyComb> getStrategyCombMap(double tem, double[] prices, List<StrategyComb> strategyCombList, String[] config) {
        Map<Double, StrategyComb> combsMap = new HashMap<>();
        if (tem < 24) {
            if (!config[0].equals("")) {
                int[] indexs = Arrays.stream(config[0].split(",")).mapToInt(item->item.equals("")?-1:Integer.parseInt(item)).toArray();
                for(int i=0;i<indexs.length;i++) {
                    int id = indexs[i];
                    if (id == -1) {
                        continue;
                    }
                    combsMap.put(prices[i], (StrategyComb) strategyCombList.stream().filter(item->item.getCode().equals(id)).toArray()[0]);
                }
            }
        } if (tem >= 24 && tem <= 28) {
            if (!config[0].equals("")) {
                int[] indexs = Arrays.stream(config[0].split(",")).mapToInt(item->item.equals("")?-1:Integer.parseInt(item)).toArray();
                for(int i=0;i<indexs.length;i++) {
                    int id = indexs[i];
                    if (id == -1) {
                        continue;
                    }
                    combsMap.put(prices[i], (StrategyComb) strategyCombList.stream().filter(item->item.getCode().equals(id)).toArray()[0]);
                }
            }
        } else if (tem > 28 && tem <=30) {
            if (!config[1].equals("")) {
                int[] indexs = Arrays.stream(config[1].split(",")).mapToInt(item->item.equals("")?-1:Integer.parseInt(item)).toArray();
                for(int i=0;i<indexs.length;i++) {
                    int id = indexs[i];
                    if (id == -1) {
                        continue;
                    }
                    combsMap.put(prices[i], (StrategyComb) strategyCombList.stream().filter(item->item.getCode().equals(id)).toArray()[0]);
                }
            }
        } else {
            if (!config[2].equals("")) {
                int[] indexs = Arrays.stream(config[2].split(",")).mapToInt(item->item.equals("")?-1:Integer.parseInt(item)).toArray();
                for(int i=0;i<indexs.length;i++) {
                    int id = indexs[i];
                    if (id == -1) {
                        continue;
                    }
                    combsMap.put(prices[i], (StrategyComb) strategyCombList.stream().filter(item->item.getCode().equals(id)).toArray()[0]);
                }
            }
        }

        return combsMap;
    }

    /**
     * 去除结点和边中不需要的部分（用于产生图谱对应的高亮和能源流动效果）
     * @param flag
     * @param node
     * @param path
     * @param pathAndNode
     */
    public void removeNodeOrEdge(boolean flag, String node, String path, PathAndNode pathAndNode) {
        if (flag) {
            String[] nodesSplit = pathAndNode.getNodeSplit();
            String[] pathSplit = pathAndNode.getPathSplit();
            if (node != null && !"".equals(node)) {
                nodesSplit = Arrays.stream(nodesSplit).filter(item->!item.contains(node)).toArray(String[]::new);
                pathAndNode.setNodeSplit(nodesSplit);
            }

            if (path != null && !"".equals(path)) {
                pathSplit = Arrays.stream(pathSplit).filter(item->!item.contains(path)).toArray(String[]::new);
                pathAndNode.setPathSplit(pathSplit);
            }
        }
    }

    public void removeNotRelashipNodeAndEdge(PathAndNode pathAndNode, String[] nodes) {
        // 获取默认高亮的node
        String[] defaultHeightNode = getDefaultHeightNode();

        String[] nodesSplit = pathAndNode.getNodeSplit();
        String[] pathSplit = pathAndNode.getPathSplit();

        nodesSplit = Arrays.stream(nodesSplit).filter(item->(Arrays.asList(nodes).contains(item) || Arrays.asList(defaultHeightNode).contains(item))).toArray(String[]::new);
        pathSplit = Arrays.stream(pathSplit).filter(item->(Arrays.asList(nodes).contains(item.split("#")[0]) && Arrays.asList(nodes).contains(item.split("#")[1])) || (Arrays.asList(defaultHeightNode).contains(item.split("#")[0]) && Arrays.asList(defaultHeightNode).contains(item.split("#")[1]))).toArray(String[]::new);
        pathAndNode.setBoth(nodesSplit, pathSplit);
    }

    public void removeNodeOrEdgeByDevice(PathAndNode pathAndNode, BaseDevice device) {
        String[] nodesSplit = pathAndNode.getNodeSplit();
        String[] pathSplit = pathAndNode.getPathSplit();
        // 如果是用能设备
        if (device instanceof ConsumeDevice) {
            List<SupplyDevice> supplyDevices = ((ConsumeDevice) device).getSupplyList();
            if (supplyDevices.stream().mapToInt(item->item.hasRelease()?1:0).sum() == 0) {
                nodesSplit = Arrays.stream(nodesSplit).filter(item->!item.equals(String.valueOf(device.getId()))).toArray(String[]::new);
                pathSplit = Arrays.stream(pathSplit).filter(item->!item.matches(".*?#" + String.valueOf(device.getId()) + "$")).toArray(String[]::new);
            }
        }
        // 如果是供能设备
        if (device instanceof SupplyDevice) {
            SupplyDevice supplyDevice = (SupplyDevice) device;
            if (!supplyDevice.hasRelease()) {
                pathSplit = Arrays.stream(pathSplit).filter(item->!item.matches("^" + String.valueOf(device.getId()) + "#" + ((SupplyDevice) device).getSupplyDeviceId())).toArray(String[]::new);
                if (supplyDevice.getStoragyDeviceName() == null) {
                    nodesSplit = Arrays.stream(nodesSplit).filter(item->!item.equals(String.valueOf(device.getId()))).toArray(String[]::new);
                }
            }
        }
        // 如果是储能设备
        if (device instanceof StorageDevice) {
            StorageDevice storageDevice = (StorageDevice) device;
            if (!storageDevice.hasStorage()) {
                if (!storageDevice.hasRelease()) {
                    nodesSplit = Arrays.stream(nodesSplit).filter(item->!item.equals(String.valueOf(device.getId()))).toArray(String[]::new);
                }
                pathSplit = Arrays.stream(pathSplit).filter(item->!item.matches(".*?#" + String.valueOf(device.getId()) + "$")).toArray(String[]::new);


                // 判断与储能设备关联的供能设备是否没有依赖
                SupplyDevice relDevice = storageDevice.getRelDevice();
                if (!relDevice.hasRelease()) {
                    nodesSplit = Arrays.stream(nodesSplit).filter(item->!item.equals(String.valueOf(relDevice.getId()))).toArray(String[]::new);
                }
            }
        }

        pathAndNode.setBoth(nodesSplit, pathSplit);
    }

    public void removeNodeOrEdgeAllDevice(PathAndNode pathAndNode, BaseDevice ...devices) {
        removeNotRelashipNodeAndEdge(pathAndNode, Arrays.stream(devices).map(item->String.valueOf(item.getId())).toArray(String[]::new));
        for (BaseDevice device: devices) {
            removeNodeOrEdgeByDevice(pathAndNode, device);
        }
    }

    /**
     * 获取整型数组中某一个数字连续出现的起始点和终止点集合
     * @param number
     * @param flag
     * @return
     */
    public List<String> getBoundary(int number, int[] flag) throws ParseException {
        List<String> boundaryList = new ArrayList<>();
        // 第一步，找到第一个数字对应的index
        int start = 0;
        int end = 0;
        for (start = 0; start<flag.length; start++) {
            if (number == flag[start]) {
                break;
            }
        }
        for (int i=start+1; i<flag.length; i++) {
            if (number == flag[i] && number != flag[i-1]) {
                start = i;
            }
            if ((number != flag[i] && number == flag[i-1]) || (number == flag[i] && i == flag.length-1)) {
                if (number != flag[i]) {
                    end = i - 1;
                } else {
                    end = i;
                }
                String dateStr = start + "~" + end;
                boundaryList.add(dateStr);
            }
        }

        // 检查首尾
        int startIndex = -1;
        int endIndex = -1;
        for(int i=0 ; i<boundaryList.size(); i++) {
            if (boundaryList.get(i).matches("^0~.*?")) {
                startIndex = i;
            }
            if (boundaryList.get(i).matches(".*?~" + (flag.length-1) + "$")) {
                endIndex = i;
            }
        }
        if (startIndex != endIndex && endIndex != -1 && startIndex != -1) {
            boundaryList.set(startIndex, boundaryList.get(startIndex).replaceFirst("^0", boundaryList.get(endIndex).split("~")[0]));
            boundaryList.remove(endIndex);
        }

        // 替换（使用了Electricity）
//        ElectricityPriceUtil utils = getElectricityUtil(getGraphId());
        ElectricityPriceUtil utils = null;

        for(int i=0 ; i<boundaryList.size(); i++) {
            boundaryList.set(i, utils.getTimebyIndex(Integer.parseInt(boundaryList.get(i).split("~")[0])) + "~" + utils.getTimebyIndex(Integer.parseInt(boundaryList.get(i).split("~")[1]) + 1));
        }

        return boundaryList;
    }

    public List<StrategyOutput> generateStrategyOutList(int number, String info, int[] flag, double[] power, Date now, Map<Integer, Strategy> codeMapping, Integer type) throws ParseException {
        List<StrategyOutput> outputList = new ArrayList<>();
        Date tomorrow = DateUtils.plusOrMinusDay(now, 1).getTime();
        List<String> boundaryList = getBoundary(number, flag);
        for (String boundary: boundaryList) {
            String[] time = boundary.split("~");
            String startTime = time[0];
            int startHour = Integer.parseInt(startTime.split(":")[0]);
            int startMinute = Integer.parseInt(startTime.split(":")[1]);
            String endTime = time[1];
            int endHour = Integer.parseInt(endTime.split(":")[0]);
            int endMinute = Integer.parseInt(endTime.split(":")[1]);

            StrategyOutput output = new StrategyOutput();

            // 起始时间 > 结束时间（表示从今天晚上到明天早上）
            if (startHour > endHour || (startHour == endHour && startMinute > endMinute)) {
                output.setStartTime(DateUtils.getDateWithHourAndMinute(now, startHour, startMinute).getTime());
            } else if (startHour == endHour && startMinute == endMinute) {
                output.setStartTime(DateUtils.getDateWithHourAndMinute(now, 23, startMinute).getTime());
                startHour = 23;
                endHour = 23;
            } else {
                output.setStartTime(DateUtils.getDateWithHourAndMinute(tomorrow, startHour, startMinute).getTime());
            }

            output.setEndTime(DateUtils.getDateWithHourAndMinute(tomorrow, endHour, endMinute).getTime());

            startTime = String.format("%02d:%02d", startHour, startMinute);
            endTime = String.format("%02d:%02d", endHour, endMinute);
            output.setInfo(startTime + "~" + endTime + " " + info);

            output.setInfoTime(startTime + "~" + endTime);
            output.setInfoContent(info);

            output.setCode(codeMapping.get(number).getCode());
            output.setStrategyType(type);

            // 是否需要填充能量值
            if (power != null && Arrays.stream(power).sum() > 0) {
                int start = startHour;
                int end = endHour;
                int count = 0;
                double sum = 0.0;

                if (start > end) {
                    for (int j=start;j<24;j++) {
                        sum += power[j];
                        count += 1;
                    }
                    start = 0;
                } else if (start == end) {
                    start = 0;
                    end = power.length-1;
                }

                for (int j=start;j<=end;j++) {
                    sum += power[j];
                    count += 1;
                }

                float average = (float) sum / count;
                output.setPower(Float.parseFloat(String.format("%.2f",average)));
            }

            outputList.add(output);
        }

        return outputList;
    }

    /**
     * 得到StrategyOut列表
     * @param device
     * @param now
     * @param codeMapping
     * @return
     */
    public List<StrategyOutput> getStrategyOutList(BaseDevice device, Date now, Map<Integer, Strategy> codeMapping, Integer type) throws ParseException {
        List<StrategyOutput> outputList = new ArrayList<>();
        // 针对供能设备和储能设备有不同的策略生成逻辑
        if (device instanceof SupplyDevice) {
            SupplyDevice supplyDevice = (SupplyDevice) device;
            outputList.addAll(generateStrategyOutList(1, supplyDevice.getSupplyInfo(), supplyDevice.getFlag(), supplyDevice.getPower(), now, codeMapping, type));
        }
        if (device instanceof StorageDevice) {
            StorageDevice storageDevice = (StorageDevice) device;
            outputList.addAll(generateStrategyOutList(2, storageDevice.getStorageInfo(), storageDevice.getFlag(),null, now, codeMapping, type));
        }

        return outputList;
    }

    public void saveAllStrategy(List<StrategyOutput> strategyOutputs) {
        strategyOutService.insertAll(strategyOutputs);
    }

    public void saveAllHistoryData(List<HistoryData> historyDataList) {
        historyDataService.insertAll(historyDataList);
    }

}